/*
 * Copyright (C) 2003-2009 eXo Platform SAS.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.reflext.test;

import org.reflext.api.TypeResolver;
import org.reflext.apt.*;
import org.reflext.core.TypeResolverImpl;

import javax.annotation.processing.SupportedSourceVersion;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.PackageElement;
import java.util.Set;
import java.util.Map;
import java.util.HashMap;
import java.lang.annotation.Annotation;

/**
 * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
 * @version $Revision$
 */
@SupportedSourceVersion(SourceVersion.RELEASE_5)
@SupportedAnnotationTypes("org.reflext.test.UnitTest")
public class AnnotationProcessorTestRunner extends AbstractProcessor {

  public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

    System.out.println("Starting tests");

    Set<? extends Element> elts = roundEnv.getElementsAnnotatedWith(UnitTest.class);

    for (Element elt : elts) {

      TypeResolver<Object> domain = TypeResolverImpl.create(new JavaxLangReflectionModel());

      PackageElement packageElt = (PackageElement)elt;

      System.out.println("Processing package " + packageElt.getQualifiedName());

      Map<String, Object> types = findTypes(packageElt);

      Map<String, Object> map = getAnnotationMap(packageElt, UnitTest.class);

      DeclaredType testType = (DeclaredType)map.get("value");

      TypeElement testElt = (TypeElement)testType.asElement();
      String name = testElt.getQualifiedName().toString();
      try {
        ClassLoader classLoader = getClass().getClassLoader();
        Class clazz = classLoader.loadClass(name);
        ReflectUnitTest test = (ReflectUnitTest)clazz.newInstance();
        TypeDomainExt<Object> td = new TypeDomainExt<Object>(domain, types);
        test.run("org.reflext.apt", td);
      }
      catch (Exception e) {
        throw new Error(e);
      }




/*
      for (AnnotationMirror annotationMirror : ee.getAnnotationMirrors()) {

        System.out.println("annotationMirror.getElementValues() = " + annotationMirror.getElementValues());

*/
/*
        TypeElement te = (TypeElement)annotationMirror.getAnnotationType().asElement();
        if (te.getQualifiedName().toString().equals(Type.class.getName())) {
          Map<String, Object> bilto = new HashMap<String, Object>();
          for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror.getElementValues().entrySet()) {
            bilto.put(entry.getKey().getSimpleName().toString(), entry.getValue().getValue());
          }

        }
*/
/*

      }
*/

      // ClassTypeInfo contextInfo =

/*
      ClassTypeInfo cti = (ClassTypeInfo)typeInfo;

      System.out.println("cti.getName() = " + cti.getName());
*/

    }

    return false;
  }

  private static Map<String, Object> findTypes(Element elt) {
    Map<String, Object> types = new HashMap<String, Object>();
    findTypes(types, elt);
    return types;
  }

  private static void findTypes(Map<String, Object> types, Element elt) {
    Map<String, Object> map = getAnnotationMap(elt, Type.class);
    if (map != null) {
      String id = (String)map.get("value");
      if (elt instanceof ExecutableElement) {
        types.put(id, ((ExecutableElement)elt).getReturnType());
      } else if (elt instanceof TypeElement) {
        types.put(id, elt);
      } else {
        throw new AssertionError();
      }
    }

    //
    for (Element enclosedElt : elt.getEnclosedElements()) {
      findTypes(types, enclosedElt);
    }
  }

  private static Map<String, Object> getAnnotationMap(Element elt, Class<? extends Annotation> annotationClass) {
    for (AnnotationMirror annotationMirror : elt.getAnnotationMirrors()) {
      TypeElement annotationTypeElt = (TypeElement)annotationMirror.getAnnotationType().asElement();
      if (annotationTypeElt.getQualifiedName().toString().equals(annotationClass.getName())) {
        Map<String, Object> map = new HashMap<String, Object>();
        for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror.getElementValues().entrySet()) {
          map.put(entry.getKey().getSimpleName().toString(), entry.getValue().getValue());
        }
        return map;
      }
    }
    return null;
  }

}